import os
import random
from math import sqrt

# game constants
from constants import *

# cocos componets used by this scene
from cocos.director import director
from cocos.scene import Scene
from cocos.layer import Layer, ColorLayer
from cocos.scenes.transitions import *
from cocos.actions import *
from cocos.sprite import Sprite
from cocos.text import Label
from cocos.cocosnode import CocosNode
from cocos.batch import BatchNode
from cocos.draw import Line

# pyglet  componets used by this scene
import pyglet
from pyglet import gl, font
from pyglet.window import key
from pyglet.gl import *

from scene_battle import *
from scene_ingame_menu import *
from scene_planet import *
from dialog_launch_ship import *
from panel_layer import *
  
      


# this class displays graphically a background, the planets, the planet labels, 
# and the fog of war, and has the code to pick a planet via mouse (and keys?)
class PlanetsLayer( Layer ):
    is_event_handler = True
    
    def __init__(self, interface):
        super( PlanetsLayer, self ).__init__()
        
        self.interface = interface 
        self.planets = self.interface.game.galaxy.planets
        self.scaleFactor = SCREEN_HEIGHT/GALAXY_SIZE 
        
        # for debug ######   
        self.src = GL_ZERO
        self.dst = GL_ZERO
        self.blendmodelist=[GL_ZERO,GL_ONE,GL_SRC_COLOR,GL_ONE_MINUS_SRC_COLOR,GL_DST_COLOR,GL_ONE_MINUS_DST_COLOR,GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_DST_ALPHA,GL_ONE_MINUS_DST_ALPHA,GL_SRC_ALPHA_SATURATE,GL_CONSTANT_COLOR,GL_ONE_MINUS_CONSTANT_COLOR,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT_ALPHA]
        ##################
        
        # add a background image
        # TODO: procedurally generate it
        self.background = Sprite('resources/stars.jpg')
        self.add(self.background,z=-1)
        
        # load the planet sprites into a batch node
        self.planetBatch = BatchNode()
        self.add(self.planetBatch,z=0)
 
        self.planetLabels = {}
        self.ownedByPlayer = 0
        
        for planet in self.planets:
           
            planetPosition = planet.position[0]*self.scaleFactor,planet.position[1]*self.scaleFactor
           
            glow = Sprite('resources/light_classic.png',scale=(0.4*planet.size))
            glow.position = planetPosition
                           
            planetSprite = Sprite('resources/planet.png',scale=(0.05*planet.size))
            planetSprite.position = planetPosition
            planetSprite.color = (random.randint(100,255),random.randint(100,255),random.randint(100,255))
                     
            mask = Sprite('resources/dark.png',scale=(0.4*planet.size))
            mask.position = planetPosition
    
            self.planetBatch.add(glow,name=planet.name+"_glow")
            self.planetBatch.add(planetSprite,name=planet.name,z=0)  
            self.planetBatch.add(mask,name=planet.name+"_dark",z=1)
              
            
            # for each planet create a label
            label = Label( planet.name.upper(),
                font_size=12,
                x=planetPosition[0],
                y=planetPosition[1]-(planet.size/2*self.scaleFactor),
                anchor_x=font.Text.CENTER,
                anchor_y=font.Text.TOP )
                
            self.planetLabels[planet.name] = label
            self.add(label,z=2)
            
        
        
    def on_key_press( self, k , m ):
        if k == key.RIGHT:
            self.src += 1 
        if k == key.LEFT:
            self.src -= 1
        if k == key.UP:
            self.dst += 1 
        if k == key.DOWN:
            self.dst -= 1
            
        #print str(self.src) + " " + str(self.dst)
            
        
            
    def pickPlanet(self,screen_x,screen_y):
        
        vx,vy = director.get_virtual_coordinates(screen_x,screen_y)
        
        galaxy_x = int(float(vx)/(self.scaleFactor))
        galaxy_y = int(float(vy)/(self.scaleFactor))
        
        for planet in self.planets:
            if self._distance((galaxy_x,galaxy_y),planet.position) < planet.size:          
                return planet

        return None   


    def update(self):

        # update color and style of planet labels
        obp = 0
        for planet in self.planets:
            if planet.owner == OWNER_PLAYER:
                obp += 1
                color =  (230,230,40,255)   
                isBold = True
            elif planet.owner == OWNER_ENEMY:
                color =  (255,40,40,255)
                isBold = True
            else:
                color =  (240,240,240,255)
                isBold = False
                
            self.planetLabels[planet.name].element.color = color
            self.planetLabels[planet.name].element.bold = isBold
            
        # update FOW by keeping track of the number of planets owned by the player
        if obp != self.ownedByPlayer:
            self.ownedByPlayer = obp

            for planet in self.planets:
                if planet.owner == OWNER_PLAYER:
                    self.planetBatch.get(planet.name+"_dark").visible = False
                    self.planetBatch.get(planet.name+"_glow").visible = False
                else:
                    self.planetBatch.get(planet.name+"_dark").visible = False
                    self.planetBatch.get(planet.name+"_glow").visible = False


        
    def on_enter(self):
        super( PlanetsLayer, self ).on_enter()
          
            
    def on_exit(self):
        super( PlanetsLayer, self ).on_exit()

        
    def _distance(self,start,end):
        return sqrt( (end[0] - start[0])**2 + (end[1] - start[1])**2 )

         
class AlarmLayer(Layer):

    def __init__( self, planetUnderAttack ):
        super(AlarmLayer, self).__init__()    
        
        self.planet = planetUnderAttack
        self.cl = ColorLayer(255,0,0,255)
        self.cl.opacity = 0
        self.add(self.cl)
             
        
        
    def on_enter(self):
        super( AlarmLayer, self ).on_enter()  
        self.schedule_interval(self.update,1)
            
    def on_exit(self):
        super( AlarmLayer, self ).on_exit()  
        self.unschedule(self.update)
        
       
    def update(self,dt, *args, **kwargs):
        if self.planet.underAttack == True:
            self.cl.opacity = 200
            self.cl.do(FadeTo(0,1))
        else:
            self.kill()
            
        

        
class GalaxyLayer(Layer):

    is_event_handler = True     #: enable pyglet's events

    def __init__( self, interface ):
        super(GalaxyLayer, self).__init__()

        self.interface = interface 
        self.scaleFactor = SCREEN_HEIGHT/GALAXY_SIZE 
        
        self.planetsLayer = PlanetsLayer(self.interface)
        self.add(self.planetsLayer,z=-3)

        self.planets = self.interface.game.galaxy.planets
        self.currentPlanet = self.planets[0]
        
        self.selectionSprite = Sprite('resources/selection.png')  
        self.add(self.selectionSprite)
        self.selectionSprite.x = self.currentPlanet.position[0] * self.scaleFactor
        self.selectionSprite.y = self.currentPlanet.position[1] * self.scaleFactor
            
        self.mouseHoverSprite = Sprite('resources/hover.png')  
        self.add(self.mouseHoverSprite)
        self.mouseHoverSprite.visible = False        
        
        # create a batch of ship sprites
        self.shipSprites = BatchNode()
        self.add(self.shipSprites,z=1)
        
        # when this is on an attack is notified to the player
        self.alarm = False

        self.panel = PanelLayer(self.currentPlanet)
        self.add(self.panel ,z=1)
       
        self.running = False
        
        self.isDragging = False
        self.routeLine = RouteLine()
        self.add( self.routeLine )
           

    def on_enter(self):
        super( GalaxyLayer, self ).on_enter()  
        
        # shedule a function to update itself (the graphic) every frame
        self.schedule(self.update)
        
        # shedule a function to update the game state every x seconds
        self.schedule_interval(self.updateGame, 0.1)

        self.running = True
        director.show_FPS = True
        
            
            
    def on_exit(self):
        super( GalaxyLayer, self ).on_exit()  
        
        self.unschedule(self.update)
        self.unschedule(self.updateGame)
        

        
    #ef on_draw(self):
    #    self.currentPlanet
             
                    
    def update(self,dt, *args, **kwargs):
        
        if self.running == False: return
      
        self.panel.currentPlanet = self.currentPlanet

        self.planetsLayer.update()
        
        
        self.alarm = False
        flyingShips=[]
        for planet in self.planets:
            
            if planet.inBattle == True:
                # if we find a planet in battle, stop everything
                # and do the battle
                battleScene = BattleScene(self.interface,planet)
                director.push( ZoomTransition(battleScene,0.1))
                
            # trigger an alarm if an enemy ship is closing to a player planet
            if planet.underAttack == True:
                print planet.name +" is under attack"
                if planet.owner == OWNER_PLAYER:
                    self.alarm = True  

                
            # keep count of flying ships around
            flyingShips += planet.flyingShips



        # update the flying ships sprite batch
        # TODO: there should be a better way to do this
        while not len(flyingShips) == len(self.shipSprites.get_children()):
            if len(flyingShips) > len(self.shipSprites.get_children()):        
                sprite = Sprite('resources/ship.png')
                self.shipSprites.add(sprite,z=1)
            elif len(flyingShips) < len(self.shipSprites.get_children()):
                self.shipSprites.remove(self.shipSprites.get_children()[0])   
             

        # update the position and visibility of flying ship sprites
        i=0
        for ship in flyingShips:
                         
            sprite=self.shipSprites.get_children()[i]
            sprite.x = ship.x * self.scaleFactor 
            sprite.y = ship.y * self.scaleFactor
            
            if ship.owner == OWNER_PLAYER:
                sprite.color = (255,255,255)
            else:
                sprite.color = (255,40,40)
                if ship.destination.owner == OWNER_PLAYER:
                    sprite.visible = ship.visible 
                
            i+=1


        
                        
    def updateGame(self,dt, *args, **kwargs):
        if self.running == False: return
        self.interface.game.updateGalaxy()
        self.interface.game.updateAI()
        
            
    def on_key_press( self, k , m ):
        if self.running == False: return
        
        '''     
        if k == key.LEFT:
            self.currentPlanetIndex -= 1
            if self.currentPlanetIndex < 0:
                self.currentPlanetIndex = len(self.interface.game.galaxy.planets) -1
                
            #self.src -= 1
            #print "SRC: " + str(self.src)  + "\tDST:" + str(self.dst) 
                
        if k == key.RIGHT:
            self.currentPlanetIndex += 1
            if self.currentPlanetIndex >= len(self.interface.game.galaxy.planets):
                self.currentPlanetIndex = 0
         '''       
            #self.src += 1
            #print "SRC: " + str(self.src)  + "\tDST:" + str(self.dst)     
                
        #if k == key.UP:
        #    self.dst += 1
        #    print "SRC: " + str(self.src)  + "\tDST:" + str(self.dst)    
        #if k == key.DOWN:
        #    self.dst -= 1
        #    print "SRC: " + str(self.src)  + "\tDST:" + str(self.dst)    
                
        if k == key.F:
            director.window.set_fullscreen( not director.window.fullscreen )

        if k == key.PAUSE or k == key.ESCAPE:
            pyglet.app.exit()
            director.push(FadeTransition(IngameMenuScene(self.interface),0.3))
            return pyglet.event.EVENT_HANDLED # this prevents ESC to pop this scene (default behaviour)

            
        # the following key handlers triggerts some debug functions  
        if k == key.L:
            print self.planets[-1].launchShip(self.planets[0],0,100,0,0)
            
              
        if k == key.P:
            # add a person to current planet
            print "Adding a person on planet " + self.currentPlanet.name
            self.currentPlanet.population += 1
                               
        if k == key.B:
            # add a random building to current planet
            random.seed()
            area = random.choice(self.currentPlanet.areas.values())
            building = random.choice([BUILDING_BASE,BUILDING_HOUSE,BUILDING_MINE,BUILDING_FARM])         
            print "Tring to build a "+BUILDING_NAMES[building]+" on planet " + self.currentPlanet.name + str(self.currentPlanet.position) +  " on area x:" + str(area.x) + " y:" + str(area.y)
            area.clear()
            print self.currentPlanet.build(building,area)  
            
        if k == key.T:
            # train a soldier on current planet
            print "Tring to train a soldier on planet " + self.currentPlanet.name
            print self.currentPlanet.addSoldier()
            
        if k == key.S:
            # build a ship on current planet
            print "Tring to build a ship on planet " + self.currentPlanet.name
            print self.currentPlanet.buildShip()
            
        if k == key.ENTER:
            # open current planet screen
            if self.currentPlanet.owner == OWNER_PLAYER:
                director.push( ZoomTransition(PlanetScene(self.interface,self.currentPlanet),0.1))
            
        if k == key.A:
            self.add(AlarmLayer(self.currentPlanet))  

                        
    def on_mouse_motion( self, x, y, dx, dy ):
        if self.running == False: return
        self.mouseHoverSprite.visible = False
        self.routeLine.visible = False

    def on_mouse_press(self, x, y, buttons, modifiers):
        #for p in self.planets:
        #    self.planetsBatchNode.get(p.name).color = (255,0,0)
            
        if self.running == False: return   
        self.isDragging = False 
        pickedPlanet = self.planetsLayer.pickPlanet(x,y)
        
        # code to drag from planet to planet
        if pickedPlanet != None:
            self.currentPlanet = pickedPlanet
            #set position of the planet selection sprite
            self.selectionSprite.x = self.currentPlanet.position[0] * self.scaleFactor
            self.selectionSprite.y = self.currentPlanet.position[1] * self.scaleFactor
            if pickedPlanet.owner == OWNER_PLAYER:
                self.isDragging = True

                
    def on_mouse_release(self, x, y, buttons, modifiers):
        if self.running == False: return 
        self.routeLine.visible = False
        self.mouseHoverSprite.visible = False  
        if self.isDragging == False: return  
        
        pickedPlanet = self.planetsLayer.pickPlanet(x,y)
        
        # code to drag from planet to planet
        if pickedPlanet != None:
            if self.currentPlanet != pickedPlanet:
                if self.currentPlanet.owner == OWNER_PLAYER:
                    destinationPlanet = pickedPlanet
                    if len(self.currentPlanet.dockedShips) != 0: 
                        print "Trying to launch a ship from planet %s to planet %s" % (self.currentPlanet.name,destinationPlanet.name)
                        self.add( LaunchShipDialog(self,self.currentPlanet,destinationPlanet),z=2)

                       
        self.routeLine.visible = False            
        self.isDragging = False
                    

    def on_mouse_drag( self, x, y, dx, dy, buttons, modifiers ):
        if self.isDragging == False: return
     
        # calculate and display the route line
        x0 = self.currentPlanet.position[0]*self.scaleFactor
        y0 = self.currentPlanet.position[1]*self.scaleFactor
                    
        x1,y1 = director.get_virtual_coordinates(x,y)

        self.routeLine.vertexes[0] = (x0,y0)
        self.routeLine.vertexes[1] = (x1,y1)

        self.routeLine.visible = True   
        
        
        # when the mouse is in dragging mode, highlight the planet under the pointer
        self.mouseHoverSprite.visible = False        
        pickedPlanet = self.planetsLayer.pickPlanet(x,y)
        if pickedPlanet != None and pickedPlanet != self.currentPlanet: 

            #set position of the hover sprite
            self.mouseHoverSprite.x = pickedPlanet.position[0] * self.scaleFactor
            self.mouseHoverSprite.y = pickedPlanet.position[1] * self.scaleFactor
            self.mouseHoverSprite.visible = True
       
                
    def _distance(self,start,end):
        return sqrt( (end[0] - start[0])**2 + (end[1] - start[1])**2 )
        
                  
# this class represents a line which is drawn between two points
# it is used when the player drags the mouse
class RouteLine(CocosNode):
    def __init__(self):
        super(RouteLine, self).__init__()
        self.color3 = (255,255,255)
        self.vertexes = [(10.0, 10.0), (100.0, 100.0) ]
        self.visible = False
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_BLEND); 

        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);	glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
        
    def draw(self):
        if not self.visible:
            return

        glLineWidth(2) #deprecated
        glColor3ub(*self.color3)
        glBegin(GL_LINE_STRIP)
        for v in self.vertexes:
            glVertex2f(*v)
        glVertex2f(*self.vertexes[0])     
        glEnd()
        glColor3ub(*(255,255,255))


                                      
def GalaxyScene(interface):
    return Scene( GalaxyLayer(interface) )



